﻿using System;
using GeoAPI.Geometries;
using NetTopologySuite.Geometries;
using NetTopologySuite.Geometries.Utilities;
using NetTopologySuite.Index;
using NetTopologySuite.Index.IntervalRTree;

namespace NetTopologySuite.Algorithm.Locate
{
	public class IndexedPointInAreaLocator : IPointOnGeometryLocator
	{
		public IndexedPointInAreaLocator(IGeometry g)
		{
			if (!(g is IPolygonal))
			{
				throw new ArgumentException("Argument must be Polygonal");
			}
			this._index = new IndexedPointInAreaLocator.IntervalIndexedGeometry(g);
		}

		public Location Locate(Coordinate p)
		{
			RayCrossingCounter rayCrossingCounter = new RayCrossingCounter(p);
			IndexedPointInAreaLocator.SegmentVisitor visitor = new IndexedPointInAreaLocator.SegmentVisitor(rayCrossingCounter);
			this._index.Query(p.Y, p.Y, visitor);
			return rayCrossingCounter.Location;
		}

		private readonly IndexedPointInAreaLocator.IntervalIndexedGeometry _index;

		private class SegmentVisitor : IItemVisitor<LineSegment>
		{
			public SegmentVisitor(RayCrossingCounter counter)
			{
				this._counter = counter;
			}

			public void VisitItem(LineSegment seg)
			{
				this._counter.CountSegment(seg.GetCoordinate(0), seg.GetCoordinate(1));
			}

			private readonly RayCrossingCounter _counter;
		}

		private class IntervalIndexedGeometry
		{
			public IntervalIndexedGeometry(IGeometry geom)
			{
				this.Init(geom);
			}

			private void Init(IGeometry geom)
			{
				foreach (IGeometry geometry in LinearComponentExtracter.GetLines(geom))
				{
					Coordinate[] coordinates = ((ILineString)geometry).Coordinates;
					this.AddLine(coordinates);
				}
			}

			private void AddLine(Coordinate[] pts)
			{
				for (int i = 1; i < pts.Length; i++)
				{
					LineSegment lineSegment = new LineSegment(pts[i - 1], pts[i]);
					double min = System.Math.Min(lineSegment.P0.Y, lineSegment.P1.Y);
					double max = System.Math.Max(lineSegment.P0.Y, lineSegment.P1.Y);
					this._index.Insert(min, max, lineSegment);
				}
			}

			public void Query(double min, double max, IItemVisitor<LineSegment> visitor)
			{
				this._index.Query(min, max, visitor);
			}

			private readonly SortedPackedIntervalRTree<LineSegment> _index = new SortedPackedIntervalRTree<LineSegment>();
		}
	}
}
